/*
 * RT-Thread Secure
 *
 * Copyright (c) 2021, Shanghai Real-Thread Electronic Technology Co., Ltd.
 *
 * All rights reserved.
 */
#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

struct rk3568_gate_pdata
{
    rt_ubase_t reg;
    int shift;
    int invert;
};
typedef struct rk3568_gate_pdata rk3568_gate_pdata_t;

static void set_enable(struct rt_clk *clk, rt_bool_t enable)
{
    rk3568_gate_pdata_t *pdata = (rk3568_gate_pdata_t *)clk->pdata;
    rt_uint32_t value = 0;

    if(enable)
    {
        value = (pdata->invert ? 0x0 : 0x1) << pdata->shift;
    }
    else
    {
        value = (pdata->invert ? 0x1 : 0x0) << pdata->shift;
    }

    setreg(pdata->reg, value);
}

static rt_bool_t get_enable(struct rt_clk *clk)
{
    rk3568_gate_pdata_t *pdata = (rk3568_gate_pdata_t *)clk->pdata;

    if(readl(pdata->reg) & (0x1 << pdata->shift))
    {
        return pdata->invert ? RT_FALSE : RT_TRUE;
    }

    return pdata->invert ? RT_TRUE : RT_FALSE;
}

static struct rt_clk_ops ops =
{
    .set_enable = set_enable,
    .get_enable = get_enable,
};

rt_clk_t *rt_clk_rk3568_gate_register(const char *name, const char *parent, rt_ubase_t reg, rt_uint32_t shift,
    rt_bool_t invert)
{
    rt_clk_t *clk = RT_NULL;
    rk3568_gate_pdata_t *pdata = RT_NULL;

    RT_ASSERT(name && parent);

    if(rt_strlen(name) == 0 || rt_strlen(parent) == 0)
    {
        clk = RT_NULL;
        goto _fail;
    }

    clk = rt_clk_create(name, &ops, 1, sizeof(rk3568_gate_pdata_t), 0);
    if(!clk)
    {
        goto _fail;
    }

    clk->parents[0] = rt_clk_find(parent);
    pdata = (rk3568_gate_pdata_t *)(clk->pdata);
#if defined(RT_USING_SMART)
    pdata->reg = (rt_ubase_t)rt_ioremap((void *)reg, 0x10000);
#else
    pdata->reg = reg;
#endif /* RT_USING_SMART */
    pdata->invert = invert;

    if(rt_clk_register(clk) != RT_EOK)
    {
        goto _fail;
    }

    return clk;

_fail:
    if(clk)
    {
        rt_free(clk);
    }

    return RT_NULL;
}

rt_err_t rt_clk_rk3568_gate_unregister(rt_clk_t *clk)
{
    rt_err_t ret = RT_EOK;

    ret = rt_clk_unregister(clk);
    if(!ret)
    {
        return ret;
    }

    rt_clk_delete(clk);

    return RT_EOK;
}

